import java.util.ArrayDeque;
import java.util.concurrent.atomic.AtomicInteger;
import jdk.internal.vm.Continuation;
import jdk.internal.vm.ContinuationScope;

// java --add-opens java.base/jdk.internal.vm=ALL-UNNAMED PrimeC4
public class PrimeC4 {
	static final ContinuationScope cs = new ContinuationScope("");
	static final FastBlockingQueue<Continuation> readyStack = new FastBlockingQueue<>(16384);

	static {
		for (int i = 0, n = Runtime.getRuntime().availableProcessors(); i < n; i++) {
			new Thread(() -> {
				try {
					for (; ; ) {
						var c = (ContinuationEx)readyStack.take();
						c.run();
						var w = c.waitChan;
						if (w != null) {
							c.waitChan = null;
							w.state.set(w.state.get() & ~1);
						}
					}
				} catch (Throwable e) {
					try {
						Thread.sleep(1000);
					} catch (InterruptedException ex) {
						throw new RuntimeException(ex);
					}
					//noinspection CallToPrintStackTrace
					e.printStackTrace();
				}
			}, "T" + i).start();
		}
	}

	static final class ContinuationEx extends Continuation {
		IntChan waitChan;

		public ContinuationEx(ContinuationScope scope, Runnable target) {
			super(scope, target);
		}
	}

	static class IntChan {
		final ArrayDeque<Continuation> waitQueue = new ArrayDeque<>();
		private int value;
		final AtomicInteger state = new AtomicInteger();

		void put(int v) {
			for (; ; ) {
				if (state.compareAndSet(0, 1)) {
					value = v;
					var c = waitQueue.poll();
					state.set(2);
					if (c != null)
						resume(c);
					break;
				}
				if (state.compareAndSet(2, 3)) {
					waitQueue.offer(Continuation.getCurrentContinuation(cs));
					pause(this);
				}
			}
		}

		int take() {
			for (; ; ) {
				if (state.compareAndSet(2, 3)) {
					var v = value;
					var c = waitQueue.poll();
					state.set(0);
					if (c != null)
						resume(c);
					return v;
				}
				if (state.compareAndSet(0, 1)) {
					waitQueue.offer(Continuation.getCurrentContinuation(cs));
					pause(this);
				}
			}
		}
	}

	static void pause(IntChan w) {
		((ContinuationEx)Continuation.getCurrentContinuation(cs)).waitChan = w;
		Continuation.yield(cs);
	}

	static void resume(Continuation c) {
		try {
			readyStack.put(c);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
	}

	static void go(Runnable r) {
		resume(new ContinuationEx(cs, r));
	}

	static void generate(IntChan ch) {
		//noinspection InfiniteLoopStatement
		for (int i = 2; ; i++)
			ch.put(i);
	}

	static void filter(IntChan in, IntChan out, int prime) {
		//noinspection InfiniteLoopStatement
		for (; ; ) {
			var i = in.take();
			if (i % prime != 0)
				out.put(i);
		}
	}

	public static void main(String[] args) throws InterruptedException {
		var count = args.length > 0 ? Integer.parseInt(args[0]) : 10000;
		go(() -> {
			var ch = new IntChan();
			var ch0 = ch;
			go(() -> generate(ch0));
			for (int i = 0; ; ) {
				var prime = ch.take();
				if (++i == count) {
					System.out.println(prime);
					System.exit(0);
				}
				var ch1 = ch;
				var ch2 = new IntChan();
				go(() -> filter(ch1, ch2, prime));
				ch = ch2;
			}
		});
		Thread.sleep(1_000_000L);
	}
}
